import cv2
import numpy as np

def detect_brown_box_live(reference_length_cm, reference_pixel_length, min_area=5000):
    """
    实时检测摄像头中的棕色快递盒子并估算其尺寸
    
    参数:
        reference_length_cm: 参考物体的实际长度(cm)
        reference_pixel_length: 参考物体在图像中的像素长度
        min_area: 最小面积阈值(像素)，小于此值的箱子将被过滤
    """
    # 定义棕色快递盒子的HSV范围
    lower_brown = np.array([5, 50, 50])     # 最小H=5(偏红), S=50, V=50
    upper_brown = np.array([20, 200, 200])  # 最大H=20(偏橙), S=200, V=200
    print("正在初始化摄像头...")
    
    # 尝试打开摄像头 (强制使用DirectShow后端)
    cap = None
    for i in [0, 1, 2]:  # 常见的前置摄像头设备号
        print(f"尝试打开摄像头设备 {i}...")
        cap = cv2.VideoCapture(i, cv2.CAP_DSHOW)  # 强制使用DirectShow
        if cap.isOpened():
            print(f"成功打开摄像头设备 {i}")
            # 设置合适的帧尺寸
            cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
            print(f"摄像头分辨率设置为: {cap.get(cv2.CAP_PROP_FRAME_WIDTH)}x{cap.get(cv2.CAP_PROP_FRAME_HEIGHT)}")
            break
        if cap:
            cap.release()
    
    if not cap or not cap.isOpened():
        print("\n无法打开任何摄像头，请按以下步骤检查:")
        print("1. 确保摄像头已正确连接")
        print("2. 检查Windows摄像头权限设置:")
        print("   - 打开Windows设置 > 隐私 > 相机")
        print("   - 确保'允许应用访问相机'已打开")
        print("   - 确保'允许桌面应用访问相机'已打开")
        print("3. 关闭可能占用摄像头的程序(如Zoom、微信等)")
        print("4. 尝试更新摄像头驱动程序")
        print("5. 使用Windows相机应用测试摄像头是否正常工作")
        
        # 测试摄像头是否被其他程序占用
        try:
            test_cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
            if test_cap.isOpened():
                print("\n测试结果: 摄像头可以被OpenCV访问，但可能被其他程序占用")
                test_cap.release()
            else:
                print("\n测试结果: 摄像头完全无法访问，请检查权限和驱动")
        except Exception as e:
            print(f"\n摄像头测试异常: {str(e)}")
        
        return
    
    # 允许3次帧获取失败
    retry_count = 0
    max_retries = 3
    
    # 计算像素到厘米的转换比例
    pixel_per_cm = reference_pixel_length / reference_length_cm
    
    while True:
        # 读取摄像头帧
        ret, frame = cap.read()
        if not ret:
            retry_count += 1
            if retry_count >= max_retries:
                print("多次尝试后仍无法获取视频帧，请检查摄像头状态")
                break
            print(f"获取视频帧失败，正在重试({retry_count}/{max_retries})...")
            continue
        
        # 重置重试计数器
        retry_count = 0
        
        # 转换到HSV颜色空间
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        
        # 创建黄色区域的掩码
        mask = cv2.inRange(hsv, lower_brown, upper_brown)
        
        # 对掩码进行形态学操作，去除噪声
        kernel = np.ones((5,5), np.uint8)
        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
        
        # 在黄色掩码上查找轮廓
        contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        # 遍历所有轮廓
        for contour in contours:
            # 计算轮廓周长和多边形近似
            peri = cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, 0.02 * peri, True)
            
            # 如果是四边形(矩形)
            if len(approx) == 4:
                # 计算边界框
                x, y, w, h = cv2.boundingRect(approx)
                
                # 计算面积并过滤小箱子
                area = w * h
                if area < min_area:
                    continue
                
                # 计算实际尺寸(cm)
                width_cm = w / pixel_per_cm
                height_cm = h / pixel_per_cm
                
                # 绘制矩形和尺寸信息
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 255), 3)
                cv2.putText(frame, f"Brown Box: {width_cm:.1f}cm x {height_cm:.1f}cm", 
                           (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 165, 255), 2)
                # 显示面积信息
                cv2.putText(frame, f"Area: {area} px", 
                           (x, y + h + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
        
        # 显示棕色区域掩码(调试用)
        cv2.imshow("Brown Mask", mask)
        
        # 显示结果
        cv2.imshow("Real-time Box Detection", frame)
        
        # 按ESC键退出
        if cv2.waitKey(1) == 27:
            break
    
    # 释放资源
    cap.release()
    cv2.destroyAllWindows()

# 示例用法
if __name__ == "__main__":
    """
    棕色快递盒子实时检测使用方法:
    1. 在摄像头前放置一个已知尺寸的参考物体(如5cm的卡片)
    2. 测量该物体在画面中的像素长度(如100像素)
    3. 运行程序后将只检测棕色物体
    4. 按ESC键退出程序

    参数说明:
    - reference_length_cm: 参考物体实际长度(cm)
    - reference_pixel_length: 参考物体像素长度
    - min_area: 最小检测面积(像素)，默认5000
    """
    detect_brown_box_live(
        reference_length_cm=5.0, 
        reference_pixel_length=100,
        min_area=5000  # 可根据需要调整
    )